package nancal.mp.config.filter

import nbcp.base.comm.JsonMap
import nbcp.base.extend.*
import org.reactivestreams.Publisher
import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction
import org.springframework.http.HttpStatus
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono

/**
 * --
 * @author yuxh
 */

class ResponseRewriteFunction(
    var paths: List<String>,
    var columns: List<String>, var noColumns: List<String>,
    var keys: List<String>, var noKeys: List<String>
) :
    RewriteFunction<String, String> {

    override fun apply(
        exchange: ServerWebExchange,
        responseBody: String?
    ): Publisher<String> {
        if (responseBody.isNullOrEmpty()) {
            return Mono.just(responseBody.AsString())
        }

        //如果不是 Json,直接返回
        if (!responseBody.startsWith("{") ||
            !responseBody.endsWith("}")
        ) {
            return Mono.just(responseBody.AsString())
        }

        var response = exchange.response;
        if (response.statusCode != HttpStatus.OK) {
            return Mono.just(responseBody.AsString())
        }

        var contentTypes = response.headers.get("Content-Type");
        if (contentTypes == null) {
            return Mono.just(responseBody.AsString())
        }
        var contentType = contentTypes.joinToString(",")
        if (!contentType.contains("json")) {
            return Mono.just(responseBody.AsString())
        }


        if (columns.isEmpty() && noColumns.isEmpty() && keys.isEmpty() && noKeys.isEmpty()) {
            return Mono.just(response.AsString());
        }


        var respMap = responseBody.FromJson<JsonMap>()!!

        if (keys.HasValue || noKeys.HasValue) {
            var mapResult = getMapData(respMap);

            if (mapResult.result.any()) {
                var removedKeys = procObjectData(mapResult.result)
                respMap.put("removedKeys", removedKeys)
            }
        }

        if (columns.HasValue || noColumns.HasValue) {
            var listResult = getListData(respMap);

            if (listResult.result.any()) {
                var removedColumns = procListColumns(listResult.result)
                respMap.put("removedColumns", removedColumns)
            }
        }

        return Mono.just(respMap.ToJson())
    }

    private fun procObjectData(
        map: MutableMap<String, *>
    ): Set<String> {

        var ret = mutableSetOf<String>()

        var removeKeys = mutableSetOf<String>()
        if (keys.any()) {
            removeKeys.addAll(map.keys.minus(keys))
        }
        if (noKeys.any()) {
            removeKeys.addAll(noKeys)
        }

        if (removeKeys.size > 0) {
            map.removeAll({ it.IsIn(removeKeys) })
        }

        ret.addAll(removeKeys)
        return ret;
    }


    class MapResult {
        var path: String = "";
        var result: MutableMap<String, *> = mutableMapOf<String, Any>();
    }

    private fun getMapData(map: JsonMap): MapResult {
        var result = MapResult();

        for (path in paths) {
            var data = map.getValueByWbsPath(path);
            if (data == null) {
                continue
            }

            if (data is Map<*, *>) {
                result.path = path;
                result.result = data as MutableMap<String, *>;
                return result
            }
        }

        return result;

    }

    private fun procListColumns(
        list: MutableList<MutableMap<String, *>>
    ): Set<String> {
        var ret = mutableSetOf<String>()
        list.forEach { row ->
            var removeKeys = mutableSetOf<String>()
            if (columns.any()) {
                removeKeys.addAll(row.keys.minus(columns))
            }
            if (noColumns.any()) {
                removeKeys.addAll(noColumns)
            }
            if (removeKeys.size > 0) {
                row.removeAll({ it.IsIn(removeKeys) })
            }

            ret.addAll(removeKeys)
        }

        return ret;
    }

    class ListResult {
        var path: String = "";
        var result: MutableList<MutableMap<String, *>> = mutableListOf();
    }

    private fun getListData(map: JsonMap): ListResult {
        var result = ListResult();

        for (path in paths) {
            var data = map.getValueByWbsPath(path);
            if (data == null) {
                continue
            }

            if (data is List<*>) {

                if (data.size == 0) {
                    return result
                }

                //验证第一条数据是不是对象。
                var first = data.firstOrNull { it != null }
                if (first == null) {
                    return result
                }

                if (!Map::class.java.isAssignableFrom(first.javaClass)) {
                    return result
                }

                result.path = path;
                result.result = data as MutableList<MutableMap<String, *>>;
                return result
            }
        }

        return result;
    }
}

